Framework Ethernet模块添加接口

EthernetService

本篇内容基于Android 6.0.1,涉及到的framework类

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetService.java
frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetServiceImpl.java
\frameworks\base\core\java\android\net\EthernetManager.java
\frameworks\base\core\java\android\net\IEthernetManager.aidl
\frameworks\base\core\java\android\net\IpConfiguration.java
/frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

以太网中在一般的Android Phone中是不存在的,不过在一些智能硬件中带有以太网的模块,因此需要有一系列的
接口来访问以太网的状态信息。比如当前的以太网的模式和连接状态等

但是,EthernetService的服务接口只是在framework层可用,并没有开放给用户。实际上看过源码可以看到一些@hide标记的
api和类,这也说明它们对用户层不可用。那么我们如何去获取以太网的状态信息呢?

本篇将介绍如何在framework中EthernetService中添加这样的接口来供用户访问,这里以添加获取当前的以太网的模式为例,我们知道Android
中Service提供服务是通过Binder来支持的,而AIDL为跨进程访问提供用户访问的接口,而以太网服务的接口是通过IEthernetManager.aidl来定义的
在SystemService启动时会去注册一系列的Service到ServiceManager中去,这里我们先看EthernetService是如何启动的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public final class SystemServer {
……
private static final String ETHERNET_SERVICE_CLASS =
"com.android.server.ethernet.EthernetService";

public static void main(String[] args) {
new SystemServer().run();
}

private void run()
{
try{
...
startOtherServices();
...
}
....
}


private void startOtherServices() {

if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
}

}
}

在SystemService中会去通过一个SystemServiceManager的startService方法来启动,从参数大概可以猜出来它会通过反射去创建Service实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

@SuppressWarnings("unchecked")
public SystemService startService(String className) {
final Class<SystemService> serviceClass;
try {
serviceClass = (Class<SystemService>)Class.forName(className);
} catch (ClassNotFoundException ex) {
Slog.i(TAG, "Starting " + className);
throw new RuntimeException("Failed to create service " + className
+ ": service class not found, usually indicates that the caller should "
+ "have called PackageManager.hasSystemFeature() to check whether the "
+ "feature is available on this device before trying to start the "
+ "services that implement it", ex);
}
return startService(serviceClass);
}

//SystemServiceManager
public <T extends SystemService> T startService(Class<T> serviceClass) {
final String name = serviceClass.getName();
Slog.i(TAG, "Starting " + name);

// Create the service.
if (!SystemService.class.isAssignableFrom(serviceClass)) {
throw new RuntimeException("Failed to create " + name
+ ": service must extend " + SystemService.class.getName());
}
final T service;
try {
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
service = constructor.newInstance(mContext);
} catch (InstantiationException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service could not be instantiated", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service must have a public constructor with a Context argument", ex);
} catch (NoSuchMethodException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service must have a public constructor with a Context argument", ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service constructor threw an exception", ex);
}

// Register it.
mServices.add(service);

// Start it.
try {
service.onStart();
} catch (RuntimeException ex) {
throw new RuntimeException("Failed to start service " + name
+ ": onStart threw an exception", ex);
}
return service;
}

通过反射创建EthernetService实例,并添加到列表中管理起来。随后调用onStart启动它。我们先看看EthernetService,它有可能就是我们的要找的Binder Server。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.android.server.ethernet;

import android.content.Context;
import android.util.Log;
import com.android.server.SystemService;

public final class EthernetService extends SystemService {
private static final String TAG = "EthernetService";
final EthernetServiceImpl mImpl;

public EthernetService(Context context) {
super(context);
mImpl = new EthernetServiceImpl(context);
}

@Override
public void onStart() {
Log.i(TAG, "Registering service " + Context.ETHERNET_SERVICE);
publishBinderService(Context.ETHERNET_SERVICE, mImpl);
}

@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mImpl.start();
}
}
}

EthernetService的实现很简单,它并不是真正的Binder Server,它的任务很简单是就是去注册一个服务EthernetServiceImpl,注册到哪去了呢?
这还用问?当然是ServiceManager中,这里EthernetService继承自SystemService,publishBinderService正是在它里面实现的,它将服务实体
注册到ServiceManager中去。那么毋庸置疑,这里的EthernetServiceImpl就是Binder Server了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* EthernetServiceImpl handles remote Ethernet operation requests by implementing
* the IEthernetManager interface.
*
* @hide
*/
public class EthernetServiceImpl extends IEthernetManager.Stub {

//在service中实现我们添加的接口
@Override
public int getEthernetMode()
{
Log.d(TAG,"getEthernetMode=="+(mIpConfiguration.ipAssignment == IpAssignment.DHCP));
return (mIpConfiguration.ipAssignment == IpAssignment.DHCP) ? 0 : 1;
}

}

从其实现来看它的确是,因为它继承了IEthernetManager.Stub,从而有了跨进程的能力。我们看看它提供了的接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/** {@hide} */
interface IEthernetManager
{
IpConfiguration getConfiguration();
void setConfiguration(in IpConfiguration config);
boolean isAvailable();
void addListener(in IEthernetServiceListener listener);
void removeListener(in IEthernetServiceListener listener);
int getEthernetConnectState();
boolean setEthernetEnabled(in boolean enable);
int getEthernetIfaceState();

//添加的服务接口
int getEthernetMode();
}

看过context的同学可能知道ContextImpl在创建的时候会去注册一些服务管理对象,这些都是被添加到静态实例中,以供多个context实例共享使用,这也是我们为什么
能够通过Context.getSystemService获取服务的原因,而EthernetService是通过EthernetManager来管理的。

1
2
3
4
5
6
7
8
registerService(Context.ETHERNET_SERVICE, EthernetManager.class,
new CachedServiceFetcher<EthernetManager>() {
@Override
public EthernetManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.ETHERNET_SERVICE);
IEthernetManager service = IEthernetManager.Stub.asInterface(b);
return new EthernetManager(ctx.getOuterContext(), service);
}});

那么我们为什么不能用Context获取getSystemService来获取EthernetService的管理者从而访问其服务接口呢?
原因是Context中不允许我们这么做,对ETHERNET_SERVICE使用了@hide,这样我们就不能取到ETHERNET_SERVICE的服务,
如果我们在代码中直接引用EthernetManager也会报错,因为EthernetManager对上层是不可见的。

public abstract class Context {

1
2
3
4
5
6
7
8
9
10
11
12
13
    2832    /**
2833 * Use with {@link #getSystemService} to retrieve a {@link
2834 * android.net.EthernetManager} for handling management of
2835 * Ethernet access.
2836 *
2837 * @see #getSystemService
2838 * @see android.net.EthernetManager
2839 *
2840 * @hide
2841 */
2842 public static final String ETHERNET_SERVICE = "ethernet";

}

这可如何是好?有一个办法,那就是通过反射,通过反射可以取到service的实例,从而进行访问service这些隐藏的api,当然这也有很大的局限性,
而且会带来可能存在的风险,比如当系统版本变动后就会带来不可预知的问题。但对于特定的硬件产品(系统确定且不会再变更)来说,这也算是一种有效的方法。为了避免可能存在的问题,我们最好添加一些先验条件来使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private static int getEthernetMode(Context context)
{
int result = -1;
if(/*一些校验条件*/) {
try {
String ETHERNET_SERVICE = (String) Context.class.getField("ETHERNET_SERVICE").get(null);
Class<?> ethernetManagerClass = Class.forName("android.net.EthernetManager");
Object ethernetManager = context.getSystemService(ETHERNET_SERVICE);
Field mService = ethernetManagerClass.getDeclaredField("mService");
mService.setAccessible(true);
Object mServiceObject = mService.get(ethernetManager);
Class<?> iEMgrClass = Class.forName("android.net.IEthernetManager");
Method[] methods = iEMgrClass.getDeclaredMethods();
for (Method ms : methods) {
if (ms.getName().equals("getEthernetMode")) {
result = (Integer) ms.invoke(mServiceObject);
break;
}
}
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
L.d("ethernet", "exception==>" + e.getMessage());
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return result;
}

###编译

编译 framework.jar

在上述的内容中我们添加了getEthernetMode以获取以太网的模式,即是动态还是静态的。我们的改动主要在aidl和service中,因此需要编译相关的模块。
首先执行

1
2
3
4
source ./build/envsetup.sh
lunch xxx
cd frameworks/base
mm -B

编译得到 framework.jar ,它的位置在
out\target\product\xxx\system\framework\framework.jar

编译完成后我们也可以看到生成的IEthernetManager.java了,它的位置在
\out\target\common\obj\JAVA_LIBRARIES\framework_intermediates\src\core\java\android\net\IEthernetManager.java
IEthernetManager.java为IEthernetManager.aidl对应的java文件,打开它就可以看到我们定义的接口

编译ethernet-service.jar

1
2
cd frameworks/opt/net/ethernet/
mm -B

编译得到ethernet-service.jar ,它的位置在
out\target\product\xxx\system\framework\ethernet-service.jar

最后将framework.jar和ethernet-service.jar push到system/framework/目录下重启后就可以在客户程序中通过getEthernetMode使用我们提供的接口服务了。

完。

坚持原创技术分享,您的支持将鼓励我继续创作!